package com.projekt.alpar;

import java.io.IOException;

import com.projekt.alpar.R;

import HatterOsztalyok.AudioFocusHelper;
import HatterOsztalyok.Database;
import HatterOsztalyok.MusicFocusable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;

public class PlayerService extends Service implements OnPreparedListener,
		OnCompletionListener, OnErrorListener, MusicFocusable {
	private final IBinder mBinder = new LocalBinder();
	private final String TAG = "PlayerService";

	private float duck_volume = 0.1f;
	private float volume = 1f;

	private MediaPlayer mPlayer;

	private enum State {
		Stopped, Preparing, Playing, Paused
	};

	private State mState = State.Stopped;

	private enum AudioFocus {
		NoFocusNoDuck, NoFocusCanDuck, Focused
	}

	private AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;

	private enum MusicSource {
		Albums, Artists, Playlist, All
	}

	private MusicSource mMusicSource;

	private String filter;

	private class MusicElement {
		public Uri id;
		public String Album;
		public String Artist;
		public String Title;
		public long dur;
	}

	private MusicElement[] music;

	private int currentTrack = -1;

	private NotificationManager mNotificationManager;
	private Notification mNotification;
	//private AudioManager mAudioManager;

	private final int NOTIFICATION_ID = 1;

	private AudioFocusHelper mAudioFocusHelper;

	private boolean binded = false;

	private Intent intent;
	public static final String BROADCAST_ACTION = "com.projekt.alpar.displayevent";

	/**
	 * Létrehozza a service-t inicializálva a változókat
	 * <p>
	 * Létrehozza a MediaPlayer-t, a NotificationBar-ban az értesítést,
	 * példányosít az AudioFocusHelper osztályból
	 */
	@Override
	public void onCreate() {
		Log.i(TAG, "onCreate meghivva");
		mPlayer = new MediaPlayer();
		mPlayer.setWakeMode(getApplicationContext(),
				PowerManager.PARTIAL_WAKE_LOCK);
		mPlayer.setOnPreparedListener(this);
		mPlayer.setOnCompletionListener(this);
		mPlayer.setOnErrorListener(this);

		mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		//mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

		if (android.os.Build.VERSION.SDK_INT >= 8)
			mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(),
					this);
		else
			mAudioFocus = AudioFocus.Focused;

		PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),
				0, new Intent(getApplicationContext(), LejatszoActivity.class),
				PendingIntent.FLAG_UPDATE_CURRENT);
		mNotification = new Notification();
		mNotification.tickerText = "Music Player";
		mNotification.icon = R.drawable.ic_stat_playing;
		mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
		mNotification.setLatestEventInfo(getApplicationContext(),
				"Music Player", "Just started", pi);
		startForeground(NOTIFICATION_ID, mNotification);

		intent = new Intent(BROADCAST_ACTION);

		registerReceiver(broadcastReceiver, new IntentFilter(
				"com.projekt.alpar.commandevent"));

		registerReceiver(broadcastReceiver, new IntentFilter(
				"android.media.AUDIO_BECOMING_NOISY"));
	}

	/**
	 * Ha startService meghívásával indítják a service-t, akkor ez a metódus
	 * meghívódik
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand meghivva");
		return 0;
	}

	/**
	 * Ha kapcsolódnak a service-hez, meghívódik ez a metódus. A binded változót
	 * true értékre állítja, hogy később tudjuk, vannak-e kapcsolódva a
	 * service-hez
	 */
	@Override
	public IBinder onBind(Intent intent) {
		Log.i(TAG, "onBind meghivva");
		binded = true;
		return mBinder;
	}

	/**
	 * Ha újrakapcsolódnak a service-hez, meghívódik ez a metódus. A binded
	 * változót true értékre állítja, hogy később tudjuk, vannak-e kapcsolódva a
	 * service-hez
	 */
	@Override
	public void onRebind(Intent intent) {
		Log.i(TAG, "onRebind meghivva");
		binded = true;
	}

	/**
	 * Ha lekapcsolódik mindenki a service-ről, meghívódik ez a metódus. A
	 * binded változó értékét false-ra állítja, és ha épp a service nem játszik
	 * le zenét, akkor leállítja a service-t
	 */
	@Override
	public boolean onUnbind(Intent intent) {
		Log.i(TAG, "onUnbind meghivva");
		binded = false;
		if (mState == State.Stopped)
			stopSelf();
		return true;
	}

	/**
	 * Ha megsemmisítődik a service, akkor felszabadítjuk a MediaPlayer által
	 * foglalt erőforrásokat
	 */
	@Override
	public void onDestroy() {
		Log.i(TAG, "onDestroy meghivva");
		mPlayer.reset();
		mPlayer.release();
		mPlayer = null;
		unregisterReceiver(broadcastReceiver);
	}

	/**
	 * Binder osztály, ennek egy példányát téríti vissza az onBind metódus
	 */
	public class LocalBinder extends Binder {
		public PlayerService getService() {
			Log.i(TAG, "getService meghivva");
			return PlayerService.this;
		}
	}

	/**
	 * Ha valamilyen hiba keletkezik a MediaPlayer-ben, ez a metódus hívodik
	 * meg, hogy lekezelje a hibát
	 */

	/**
	 * Ha a végére ér a lejátszó az aktuális zenének, akkor ha van még a
	 * listában elem, akkor el kezdi lejátszani a következőt, hanem leállítja a
	 * lejátszást
	 */

	/**
	 * Ha betöltötte a zenét, akkor meghívja a configAndStartMediaPlayer
	 * metódust, ami ha minden feltétel teljesül, el kezdi lejátszani a zenét
	 */

	/**
	 * Megváltoztatja a lejátszási listát, és betölti a zenék adatait
	 * 
	 * @param source
	 *            A lejátszási lista típusa
	 * @param count
	 *            Hányadik számot kell lejátssza a listából
	 * @param filt
	 *            Szűrő
	 */
	public void changeSource(int source, int count, String filt) {
		switch (source) {
		case 0:
			mMusicSource = MusicSource.Albums;
			break;
		case 1:
			mMusicSource = MusicSource.Artists;
			break;
		case 2:
			mMusicSource = MusicSource.Playlist;
			break;
		case 3:
			mMusicSource = MusicSource.All;
			break;
		}

		currentTrack = count;

		filter = filt;

		loadMusic();
	}

	/**
	 * Betölti az adatbázisból a zenék adatait a forrás és a filter függvényében
	 */
	private void loadMusic() {
		Database adat = new Database(getApplicationContext(),
				getContentResolver(), 1);
		Cursor c = adat.getMusic(mMusicSource.ordinal(), filter);

		if (c == null)
			Log.e(TAG, "Lekerdezes==null");
		else if (!c.moveToFirst())
			Log.e(TAG, "No query result");
		else {
			music = new MusicElement[c.getCount()];
			Log.i(TAG, Integer.toString(c.getCount()));
			int k = 0;
			do {
				music[k] = new MusicElement();
				music[k].id = ContentUris
						.withAppendedId(
								android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
								c.getLong(c.getColumnIndex("id")));
				music[k].Album = c.getString(c.getColumnIndex("album"));
				music[k].Artist = c.getString(c.getColumnIndex("artist"));
				music[k].Title = c.getString(c.getColumnIndex("title"));
				music[k].dur = c.getLong(c.getColumnIndex("duration"));
				k++;
			} while (c.moveToNext());
		}
		adat.close();
	}

	/**
	 * Ha a lejátszás szünetelve van vagy leállítva, elindítja; ha épp lejátszás
	 * állapotban van, akkor pedig szüneteli
	 */
	public void togglePlayback() {
		if ((mState == State.Paused) || (mState == State.Stopped))
			play();
		else
			pause();
	}

	/**
	 * Elindítja a zene lejátszását
	 */
	public void play() {
		tryToGetAudioFocus();
		Log.i("PlayerService", "mState = " + Integer.toString(mState.ordinal()));
		if (mState == State.Stopped) {
			playSong();
		} else if (mState == State.Paused) {
			mState = State.Playing;
			updateNotification();
			configAndStartMediaPlayer();
		}
	}

	/**
	 * Szüneteli a zene lejátszását
	 */
	public void pause() {
		if (mState == State.Playing) {
			mState = State.Paused;
			mPlayer.pause();
			updateNotification();
		}
	}

	/**
	 * Leállítja a zene lejátszását
	 */
	public void stop() {
		if (mState == State.Playing || mState == State.Paused) {
			mState = State.Stopped;

			mPlayer.reset();
			giveUpAudioFocus();
			updateNotification();

			if (binded == false)
				stopSelf();
		}
	}

	/**
	 * A következő számra ugrik a listában
	 */
	public void next() {
		if ((currentTrack >= 0) && (currentTrack < music.length - 1)) {
			currentTrack++;
			playSong();
		} else {
			stop();
		}
	}

	/**
	 * Az előző számra ugrik a listában
	 */
	public void previous() {
		if (currentTrack > 0) {
			currentTrack--;
			playSong();
		} else {
			mState = State.Stopped;
			mPlayer.reset();
		}
	}

	/**
	 * Az aktuálisan játszott zenében a kívánt pozícióra ugrik
	 * 
	 * @param to
	 *            Hova kell ugorjon
	 */
	public void seekTo(int to) {
		if ((mState == State.Playing) || (mState == State.Paused))
			mPlayer.seekTo(to);
	}

	/**
	 * Feladja az audiofocus-t
	 */
	private void giveUpAudioFocus() {
		if (mAudioFocus == AudioFocus.Focused && mAudioFocusHelper != null
				&& mAudioFocusHelper.abandonFocus())
			mAudioFocus = AudioFocus.NoFocusNoDuck;
	}

	/**
	 * Elindítja a lejátszás folyamatát azzal, hogy betölti a zenét
	 */
	private void playSong() {
		try {
			if (currentTrack < 0)
				changeSource(3, 0, null);

			mPlayer.reset();

			mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			mPlayer.setDataSource(getApplicationContext(),
					music[currentTrack].id);

			mState = State.Preparing;
			mPlayer.prepareAsync();

			intent.putExtra("artist", music[currentTrack].Artist);
			intent.putExtra("album", music[currentTrack].Album);
			intent.putExtra("title", music[currentTrack].Title);
			intent.putExtra("count", Integer.toString(currentTrack + 1) + "/"
					+ Integer.toString(music.length));
			intent.putExtra("duration", music[currentTrack].dur);
			intent.putExtra("position", mPlayer.getCurrentPosition());
			sendBroadcast(intent);
		} catch (IllegalArgumentException e) {
			Log.e(TAG, e.getMessage());
		} catch (SecurityException e) {
			Log.e(TAG, e.getMessage());
		} catch (IllegalStateException e) {
			Log.e(TAG, e.getMessage());
		} catch (IOException e) {
			Log.e(TAG, e.getMessage());
		}

	}

	/**
	 * Audiofocus-t kér
	 */
	private void tryToGetAudioFocus() {
		if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null
				&& mAudioFocusHelper.requestFocus())
			mAudioFocus = AudioFocus.Focused;
	}

	/**
	 * A MusicFocusable interfész egyik metódusának implementációja. Ha megkapja
	 * az audiofocus-t, meghívódik ez a metódus, ami elindítja a lejátszást
	 */

	/**
	 * A MusicFocusable interfész egyik metódusának implementációja. Ha elveszti
	 * az audiofocus-t, meghívódik ez a metódus, ami beállítja az mAudioFocus
	 * változót, majd meghívja a configAndStartMediaPlayer metódust, ami ennek
	 * függvényében elvégzi a szükséges műveleteket
	 */

	/**
	 * Frissíti a NotificationBar-ban megjelenő értesítéseket a lejátszó
	 * állapotának függvényében
	 */
	private void updateNotification() {
		PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),
				0, new Intent(getApplicationContext(), LejatszoActivity.class),
				PendingIntent.FLAG_UPDATE_CURRENT);
		String text = "";
		switch (mState) {
		case Paused:
			text = "(Paused)";
			break;
		case Playing:
			text = "(Playing)";
			break;
		case Stopped:
			text = "(Stopped)";
			break;
		}
		mNotification.setLatestEventInfo(getApplicationContext(),
				"Music Player", Integer.toString(currentTrack + 1) + "/"
						+ Integer.toString(music.length) + " "
						+ music[currentTrack].Title + " " + text, pi);
		mNotificationManager.notify(NOTIFICATION_ID, mNotification);

	}

	/**
	 * Az audiofocus függvényében elindítja a lejátszást
	 */
	private void configAndStartMediaPlayer() {
		if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
			if (mPlayer.isPlaying())
				mPlayer.pause();
			return;
		} else if (mAudioFocus == AudioFocus.NoFocusCanDuck)
			mPlayer.setVolume(duck_volume, duck_volume);
		else
			mPlayer.setVolume(volume, volume);

		if (!mPlayer.isPlaying())
			mPlayer.start();
	}

	/**
	 * Visszatéríti, hogy melyik albumon található az aktuálisan játszott zene
	 */
	public String getAlbum() {
		if (currentTrack >= 0)
			return music[currentTrack].Album;
		else
			return "";
	}

	/**
	 * Visszatéríti, hogy melyik előadó játssza az aktuálisan játszott zenét
	 */
	public String getArtist() {
		if (currentTrack >= 0)
			return music[currentTrack].Artist;
		else
			return "";
	}

	/**
	 * Visszatéríti, hogy az aktuálisan játszott zene címét
	 */
	public String getTitle() {
		if (currentTrack >= 0)
			return music[currentTrack].Title;
		else
			return "";
	}

	/**
	 * Visszatéríti, hogy az aktuálisan játszott zene hányadik a listában
	 */
	public String getTrack() {
		if (currentTrack >= 0)
			return Integer.toString(currentTrack + 1) + "/"
					+ Integer.toString(music.length);
		else
			return "";
	}

	/**
	 * Visszatéríti, hogy az aktuálisan játszott zene hosszát
	 */
	public long getDuration() {
		if (currentTrack >= 0)
			return music[currentTrack].dur;
		else
			return 0;
	}

	/**
	 * Visszatéríti a lejátszás pozícióját
	 */
	public int getPosition() {
		if (currentTrack >= 0)
			return mPlayer.getCurrentPosition();
		else
			return 0;
	}

	/**
	 * Fogadja az üzeneteket
	 */
	private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals(
					android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
				pause();
			} else {
				changeSource(intent.getIntExtra("source", 0),
						intent.getIntExtra("count", 0),
						intent.getStringExtra("filter"));
				playSong();
			}
		}
	};

	public boolean onError(MediaPlayer mp, int what, int extra) {
		mState = State.Stopped;
		mPlayer.reset();
		mPlayer.release();
		mPlayer = null;
		mPlayer = new MediaPlayer();
		mPlayer.setWakeMode(getApplicationContext(),
				PowerManager.PARTIAL_WAKE_LOCK);
		return true;
	}

	public void onCompletion(MediaPlayer mp) {
		if (currentTrack < music.length - 1) {
			currentTrack++;
			playSong();
		} else {
			stop();
		}
	}

	public void onGainedAudioFocus() {
		Log.i(TAG, "GainedAudioFocus");
		mAudioFocus = AudioFocus.Focused;

		if (mState == State.Playing)
			configAndStartMediaPlayer();
	}

	public void onLostAudioFocus(boolean canDuck) {
		Log.i(TAG, "LostAudioFocus "
				+ (canDuck ? "NoFocusCanDuck" : "NoFocusNoDuck"));
		mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck
				: AudioFocus.NoFocusNoDuck;

		if (mPlayer != null && mPlayer.isPlaying())
			configAndStartMediaPlayer();
	}

	public void onPrepared(MediaPlayer mp) {
		mState = State.Playing;
		updateNotification();
		configAndStartMediaPlayer();
	}
}
